#!/usr/bin/perl -w
#Script ListarV5 del TP-Grupo 9 Sistemas Operativos - FIUBA
#
#Autor: Pablo M. Angelani 92.707 y Florencia Bosch

require "ListarUI.pl";
use File::Copy;
use Getopt::Std;

# FILTRADOS DE LISTADO
use constant ARCHIVO => "Archivo";
use constant CICLO => "Ciclo";
use constant PATRON => "Patron";
use constant SISTEMA => "Sistema";
use constant EXPRESION => "Expresion_regular";

use constant NRO_REGISTRO => "Nro_registro";
use constant RESPUESTA => "Respuesta";
use constant NOMBRE => "Nombre";
use constant CANTIDAD => "Cantidad";

$RG_IFS = ",";
$RD_IFS = "+-#-+";
$PROCDIR="$ENV{'PROCDIR'}";
#
# Escapea cualquier meta-caractere que pudiera contener el IFS para
# poder aplicarlo en una regex.
#
sub escapearIFS{
	$ifs_ref = shift @_;
	my @a_ifs = split('', ${$ifs_ref});
	my @tmp = "";
	foreach my $c (@a_ifs){
		if (${$ifs_ref} =~ m/[\^ \$ \( \) \\ \| \@ \[ \{ \? \. \+ \*]/){
			push(@tmp, "\\");		
		} 	
		push(@tmp, $c);
	}
	${$ifs_ref} = join('',@tmp);
}

#
# Abre un nuevo archivo, donde seran guardados los resultados del informe
# generado durante la ejecución del comando. 
# Garantiza que se cree un archivo nuevo ( si hay espacio en disco) y que 
# no se pise ningun archivo ya existente de la carpeta $REPODIR.
#
# Nombre archivo: salida_xxx; donde xxx es un descriptor siempre distinto.
#
sub abrirArchivo{
	
	return 0;
}

sub analizarPosiblesResultados{
	my $filtro = shift(@_);	my $argumento = shift(@_); 
	my $resultados_ref = shift(@_);	my $ifs = "";
	#Abrir directorio de archivos procesados	
	opendir(DIR, "$PROCDIR");
	
	while (my $path = readdir(DIR)) {
	    # Ignorar archivos que no corresponan a resultados, o 
		# en caso de ser el filtro PATRON que no corresponan a ese patron.
	    next if ($path !~ m/resultado/ || $path =~ m/~/ ||
					($filtro eq PATRON && $path !~ m/($argumento)$/));
		
		#Extraigo el id_patron del path, la regex busca los los ultimos
		#caracteres alfanumericos (como el archivo es .PAT_ID, se obtiene el 
		#PAT_ID en la variable especial $1).
		$path =~ m/(\w+)$/; 
		$patron = $1;
		$path = "$PROCDIR"."/"."$path";	
		#Analizo el archivo
		open(FILE, "$path");
		$ifs = $RD_IFS;
		#Escapeo cualquier meta-caracter del IFS
		escapearIFS(\$ifs);
		while ($reg = <FILE>){
			my $posible = FALSE;
			($ciclo, $arch, $nro_reg, $res) = split("$ifs", "$reg");
			#Saco el \n del final.
			$res = substr("$res", 0, length("$res") - 1);
			#Evaluo las condiciones en que se deberia considerar como
			#potencial resultado el registro leido	
			if(($filtro eq PATRON) || 
				($filtro eq CICLO && "$ciclo" == "$argumento") ||
					($filtro eq ARCHIVO && "$arch" eq "$argumento")){
				$posible = TRUE;
			}
			
			#Guardo el registro
			if($posible){
				if (!defined ${$resultados_ref}{$patron}){
					${$resultados_ref}{$patron}= []};
				push(@{${$resultados_ref}{$patron}}, {(PATRON => $patron, 
						CICLO => "$ciclo", ARCHIVO => "$arch", 
							NRO_REGISTRO => "$nro_reg", RESPUESTA => "$res")});
			}
		}
		close(FILE);
	}
	closedir(DIR);
}

#
# Muestra un listado con ARCHIVOS, PATRONES y/o CICLOS (parametros
# de filtrado -definidos al comienzo del archivo-).
# Params:
#		$_[0]:
#			Filtros seleccionado. Valores Posibles: ARCHIVO, PATRON y CICLO.
#		$_[1]:
#			Argumento del filtro.
#
sub encontrarPosiblesResultados{
	my $filtro = shift(@_);	my $argumento = shift(@_); my $salida = shift(@_); my %resultados = ();
	my %ciclos = (); my %archivos = ();

	analizarPosiblesResultados($filtro, $argumento, \%resultados);
	
	if (!scalar %resultados){
		notificacionBusquedaVacia();
		return;
	}
	#Genero las listas de archivos y ciclos potencialemnte deseados
	#por el usuario, resultado de la busqueda anterior, para poder
	#mostrarselos al usuarios por pantalla.
	foreach my $pat (keys %resultados){
		foreach my $reg (@{$resultados{$pat}}){
			$archivos{$$reg{ARCHIVO}} = TRUE;
			$ciclos{$$reg{CICLO}} = TRUE;
		}
	}
	print "A continuacion se presenta el listado de resultados posibles:\n";
	if ($filtro eq CICLO || $filtro eq ARCHIVO){
		menuSeleccionResultados(PATRON, \%resultados, \@pat_selec);
	}
	if ($filtro eq PATRON || $filtro eq CICLO){
		menuSeleccionResultados(ARCHIVO, \%archivos, \@arc_selec);
	}
	if ($filtro eq PATRON || $filtro eq ARCHIVO){
		menuSeleccionResultados(CICLO, \%ciclos, \@cic_selec);
	}

	presentacionResultados($filtro, $argumento, $salida);
	mostrarResultados(PATRON, \%resultados, \@pat_selec, $salida);
	mostrarResultados(ARCHIVO, \%resultados, \@arc_selec, $salida);
	mostrarResultados(CICLO, \%resultados, \@cic_selec, $salida);
}

sub reemplazar{
	my $array = shift(@_); my $expresion = shift(@_); my $opcion = shift(@_);
	my $i = 0;
	while($i <= @{$array}){
		if(defined($$array[$i])){
			if($$array[$i] =~ m/$expresion/){
				if((($opcion eq "c") && ($expresion gt $$array[$i])) ||(($opcion eq "d") && ($expresion lt $$array[$i]))){
				$$array[$i] = $expresion;}
				return 1;
			} 
		}
		$i++; 
	}
	return 0;
}

sub resolverConsultaE{
	my $argumentos = shift(@_); my $desde = shift(@_); my $hasta = shift(@_);
	my $resultados = shift(@_); my $regs = shift(@_);
	
	#Obtengo los registros del nombre de archivo y cantidad de hallazgos		
	my $archivo = $$regs[1];				
	my $cantidad = $$regs[6];
	chomp($archivo);chomp($cantidad);
	if(defined $hasta && length $hasta > 0){
		if($cantidad <= $hasta){ $a = TRUE;}
		else{ $a = FALSE; } 				
	}
	elsif(defined $hasta) {$a = TRUE; }	
	if(($cantidad >= $desde) && ($a)){
	#Determinos los archivos sobre los cuales se le aplicará la consulta
		if(length ${$argumentos}{ARCHIVO} > 0){ 
			if(${$argumentos}{ARCHIVO} =~ m/$archivo/){
				if (exists($$resultados{ARCHIVO}{NOMBRE})){
					if(!($$resultados{ARCHIVO}{NOMBRE} =~ m/$archivo/)){
						$$resultados{ARCHIVO}{NOMBRE} .= ",".$archivo;
						$$resultados{ARCHIVO}{CANTIDAD} .= ",".$cantidad;
					}
				}
				else{
					$$resultados{ARCHIVO}{NOMBRE} .= ",".$archivo;
					$$resultados{ARCHIVO}{CANTIDAD} .= ",".$cantidad;
				}
			}
		}
		#Aplico la consulta sobre todos los archivos
		else {
			$$resultados{ARCHIVO}{NOMBRE} .= ",". $archivo;
			$$resultados{ARCHIVO}{CANTIDAD} .= ",". $cantidad; 
		}
	}	
}


sub resolverConsultaCD{
	my $argumentos = shift(@_); my $cantExp = shift(@_); my $resultados = shift(@_); 
	my $consulta = shift(@_); my $regs = shift(@_); my $expresiones = shift(@_);

	#Obtengo las expresiones regulares y voy almacenando las N mayores/menores
	#en un array junto con su cantidad de hallazgos. 		
	my $expresion = $$regs[2]; my $cantidad = $$regs[6]; 
	chomp($expresion);chomp($cantidad);
	$expresion = $cantidad.",".$expresion;				
	if(scalar @{$expresiones} < $cantExp){ 
		if(!reemplazar($expresiones,$expresion,$consulta)){
			push(@{$expresiones}, $expresion);} 
	}
	else{
	#Veo si se trata de una consulta de mayores o menores
		@{$expresiones} = sort(@{$expresiones});
		if($consulta eq "c"){ 
			#Para consulta de mayor chequeo el menor elem(el primero)	
			if(!reemplazar($expresiones,$expresion,$consulta)){  
				my $exp = $$expresiones[0];
				if($expresion gt $exp){
					$exp = shift(@{$expresiones});
					unshift(@{$expresiones},$expresion); 
				}
			}
		}
		else{
			#Para consulta de menor chequeo el mayor elem(el ultimo)
			if(!reemplazar($expresiones,$expresion,$consulta)){	
				my $exp = $$expresiones[$#$expresiones];
				if($expresion lt $exp){
					$exp = pop(@{$expresiones}); 
					push(@{$expresiones},$expresion);
				}
			}
		}
	} 	
}				

sub resolverConsultaAB{
	my $filtros = shift(@_); my $argumentos = shift(@_); my $resultados = shift(@_); my $consulta = shift(@_);
	my $regs = shift(@_); my $patronID = shift(@_);
		
	my $archivo = $$regs[1]; my $cantidad = $$regs[6]; my $sistemas;
	chomp($cantidad);
	#Analizo para cada filtro si se cumple con la consulta
	foreach $filtro (keys %{$argumentos}){ 
		#Si el filtro es Sistema entonces cargo los sistemas que tienen ese ID de patron
		if($filtro eq "SISTEMA"){ 
			foreach my $sis (keys %{${$argumentos}{$filtro}}){  
				if(length ${${$argumentos}{$filtro}}{$sis} > 0){
					if(${${$argumentos}{$filtro}}{$sis} =~ m/$patronID/){
						$sistemas .= ",".$sis;} 
				}
			}	
		}
		#Si para este filtro en argumentos tiene cargado elementos y estos no coinciden
		# con el del registro actual, entonces no se analiza
		if(length ${$argumentos}{$filtro} > 0){
			if(($filtro eq "ARCHIVO") && !(${$argumentos}{$filtro} =~ m/$archivo/)){ next;}
			if(($filtro eq "PATRON") && !(${$argumentos}{$filtro} =~ m/$patronID/)){ next;}
		}
		#Analizo la consulta "a", si hay una cantidad cargada en resultados comparo que esta 
		#cantidad de hallazgos sea mayor, de lo contrario cargo esta cantidad en resultados
		if($consulta eq "a"){
			if(exists(${$resultados}{$filtro}{CANTIDAD}) && exists(${$resultados}{$filtro}{NOMBRE})){
				if(${$resultados}{$filtro}{CANTIDAD} <= $cantidad){ 
					${$resultados}{$filtro}{CANTIDAD} = $cantidad;
					${$resultados}{$filtro}{NOMBRE} .= ",".$patronID if(($filtro eq "PATRON") && !(${$resultados}{$filtro}{NOMBRE} =~ m/$patronID/));
					${$resultados}{$filtro}{NOMBRE} .= ",".$archivo if($filtro eq "ARCHIVO") && !(${$resultados}{$filtro}{NOMBRE} =~ m/$archivo/);
					${$resultados}{$filtro}{NOMBRE} .= $sistemas if($filtro eq "SISTEMA") && !(${$resultados}{$filtro}{NOMBRE} =~ m/$sistemas/);  
				}
			}
			else{ 
				${$resultados}{$filtro}{NOMBRE} .= ",".$patronID if($filtro eq "PATRON"); 
				${$resultados}{$filtro}{NOMBRE} .= ",".$archivo if($filtro eq "ARCHIVO");
				${$resultados}{$filtro}{NOMBRE} .= $sistemas if(($filtro eq "SISTEMA") && (length $sistemas > 0));
				${$resultados}{$filtro}{CANTIDAD} = $cantidad;  
			}	
		}
		#Analizo la consulta "b", si la cantidad de hallazgos es 0 entonces cargo a este filtro en resultados
		elsif(($consulta eq "b") && ($cantidad == 0)){
			${$resultados}{$filtro}{NOMBRE} .= ",".$patronID if(($filtro eq "PATRON") && !(${$resultados}{$filtro}{NOMBRE} =~ m/$patronID/));
			${$resultados}{$filtro}{NOMBRE} .= ",".$archivo if(($filtro eq "ARCHIVO")&& !(${$resultados}{$filtro}{NOMBRE} =~ m/$archivo/));
			${$resultados}{$filtro}{NOMBRE} .= $sistemas if(($filtro eq "SISTEMA") && (length $sistemas > 0) &&!(${$resultados}{$filtro}{NOMBRE} =~ m/$sistemas/)); 
			${$resultados}{$filtro}{CANTIDAD} = $cantidad; 
		}
	}
}

sub analizarConsulta{
	my $filtros = shift(@_); my $argumentos = shift(@_); my $resultados = shift(@_);
	my $consulta = shift(@_); my $desde_cant = shift(@_); my $hasta = shift(@_); 
	my @expresiones;

	opendir(DIR, "$PROCDIR");
	if((($consulta eq "e") && exists(${$argumentos}{ARCHIVO})) || (($consulta =~ m/[c d]/i) && exists(${$argumentos}{EXPRESION})) || ($consulta =~ m/[a b]/i)){ 
		#Recorro todos los archivos rglobales		
		while (my $path = readdir(DIR)) {
	    	# Ignorar archivos que no corresponan a rglobales 
			next if ("$path" !~ m/rglobales/ );
			$path = "$PROCDIR"."/"."$path";	
			open(FILE,"$path");
			$ifs = $RG_IFS;
			#Escapeo cualquier meta-caracter del IFS
			escapearIFS(\$ifs);
			while($reg=<FILE>){ 
				my @regs  = split($ifs, $reg);
				if($consulta =~ m/[a b]/i) { 
					$path =~ m/(\w+)$/; my $patronID = $1;
					resolverConsultaAB($filtros,$argumentos,$resultados,$consulta,\@regs,$patronID);
				}
				if($consulta =~ m/[c d]/i) { 
					resolverConsultaCD($argumentos,$desde_cant,$resultados,$consulta,\@regs,\@expresiones);
				}	
				if($consulta eq "e") {
					resolverConsultaE($argumentos,$desde_cant,$hasta,$resultados,\@regs);}
				}	
			close(FILE);
			}
		if($consulta =~ m/[c d]/i) { 
			#Cargo el vector en el resultado
			foreach $expres (@expresiones){
				@exp = split(",",$expres); 
				${$resultados}{EXPRESION}{NOMBRE} .= ",".$exp[1];
				${$resultados}{EXPRESION}{CANTIDAD} .= ",".$exp[0];
			}	
		}
	}	
	closedir(DIR);
}

sub evaluarConsultaG{

	my $entrada = shift(@_); my $filtros_ref = shift(@_); my $argumentos = shift(@_);
	my $resultados = shift(@_);
	
	if (($entrada eq "a") || ($entrada eq "b")) {
		print "Consulta sobre patrones,sistemas,archivos.\n";		
		determinarFiltrosArgumentos($filtros_ref,$argumentos);
		analizarConsulta($filtros_ref,$argumentos,$resultados,$entrada);
	}
	elsif (($entrada eq "c") ||($entrada eq "d")) {
		print "Consulta sobre expresiones regulares.\n";
		my $cantidad = determinarCantExpresiones();
		if($cantidad ne "q"){		
			push(@{$filtros_ref},EXPRESION);${$argumentos}{EXPRESION} = ();
			analizarConsulta($filtros_ref,$argumentos,$resultados,$entrada,$cantidad); 
		}
	}
	elsif ($entrada eq "e"){
		print "Consulta rango de archivo\n";
		my $desde; my $hasta; 
		determinarArchivosRango($filtros_ref,$argumentos,\$desde,\$hasta);
		analizarConsulta($filtros_ref,$argumentos,$resultados,$entrada,$desde,$hasta); 
	}
	elsif ($entrada eq "q" ) {
		return;
	}
	
}

#############################################################
#######################    SCRIPT    ########################

my$salida="";
# Declaracion de las opciontes del comando perl que se permiten.
my %opt=();
getopts("hgrx", \%opt);

verAyuda() if defined $opt{h};

verificarErroresDeUso(\%opt);

if(!"$PROCDIR"){
	print "Ambiente no inicializado.\n";
	exit 1;
}
#Settear si se debe escribir en archivo o no
if ($opt{"x"}){
	#Creo el archivo a escribir y actualizo el valor de SECUENCIA1
	open(my $viejo, "$ENV{CONFDIR}/InstalaV5.conf") or die "No se pudo leer el archivo: $!";
	open(my $nuevo, ">$ENV{CONFDIR}/InstalaV5.conf.new") or die "No se pudo escrinir el archivo: $!";

	while(my $linea=<$viejo>){
		if ($linea =~ m/SECUENCIA1/){
			@reg = split("=", $linea);
			$salida="$ENV{'REPODIR'}/salida_$reg[1]";
			$ENV{'SECUENCIA1'}=$reg[1]+1;
			$linea =~ s/^(\w+)=(\d+)/$1=$ENV{'SECUENCIA1'}/;
		}
		print $nuevo $linea;
	}

	close $viejo;
	close $nuevo;

	move("$ENV{CONFDIR}/InstalaV5.conf.new", "$ENV{CONFDIR}/InstalaV5.conf") or die "No se pudo reescribir el archivo InstalaV5.conf.";
};

my $filtro = ""; my $argumento = "";
if ($opt{r}){
	menuOpcionR(\$filtro, \$argumento);
	encontrarPosiblesResultados($filtro, $argumento, $salida);
} else { 
	#Opcion default -g
	my %resultados;	my @filtros; my %argumentos;
	my $consulta = menuOpcionesG();
	evaluarConsultaG($consulta,\@filtros,\%argumentos, \%resultados);
	listarResultadosGlobales($consulta,\%resultados, $salida);

}




